

package top.continew.admin.question.dify.workflow;

import com.alibaba.fastjson2.JSON;
import io.github.imfangs.dify.client.DifyWorkflowClient;
import io.github.imfangs.dify.client.callback.WorkflowStreamCallback;
import io.github.imfangs.dify.client.enums.FileTransferMethod;
import io.github.imfangs.dify.client.enums.FileType;
import io.github.imfangs.dify.client.event.WorkflowFinishedEvent;
import io.github.imfangs.dify.client.model.file.FileInfo;
import io.github.imfangs.dify.client.model.workflow.WorkflowRunRequest;
import top.continew.admin.question.dify.DifyClientUtil;
import top.continew.admin.question.dify.workflow.req.QuestionAnswerReq;
import top.continew.admin.question.dify.workflow.req.QuestionGenerateReq;
import top.continew.admin.question.dify.workflow.req.QuestionSummaryReq;
import top.continew.admin.question.dify.workflow.resp.QuestionSummaryResp;
import top.continew.admin.question.manger.service.model.SaveQuestion;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

/**
 * 题目工作流处理类
 * 封装了与 Dify 工作流交互的方法，用于题目识别、答案获取和知识点提取。
 */
public class QuestionWorkflow {

    // 默认的用户，用于 Dify 工作流请求
    private static final String USER_ADMIN = "admin";
    // 工作流执行的超时时间，单位：分钟
    private static final long TIMEOUT_MINUTES = 10;
    // Dify 工作流输出中包含文本内容的键名
    private static final String OUTPUT_TEXT_KEY = "text";

    /**
     * 执行 Dify 工作流并等待其完成。
     * 这是一个私有辅助方法，用于封装工作流执行的通用逻辑。
     *
     * @param client         DifyWorkflowClient 实例，用于运行工作流。
     * @param request        包含工作流输入的 WorkflowRunRequest 对象。
     * @param outputConsumer 一个 Consumer 函数，用于处理工作流的字符串输出。
     * @throws IOException          如果发生 I/O 错误。
     * @throws InterruptedException 如果当前线程在等待时被中断。
     */
    private static void runWorkflowAndAwaitCompletion(DifyWorkflowClient client,
                                                      WorkflowRunRequest request,
                                                      Consumer<String> outputConsumer) throws IOException, InterruptedException {
        CountDownLatch latch = new CountDownLatch(1);
        StringBuilder outputBuilder = new StringBuilder();

        // 定义工作流流式回调，当工作流完成时捕获输出并释放锁
        WorkflowStreamCallback callback = new WorkflowStreamCallback() {
            @Override
            public void onWorkflowFinished(WorkflowFinishedEvent event) {
                if (event.getData() != null && event.getData().getOutputs() != null) {
                    outputBuilder.append(event.getData().getOutputs().get(OUTPUT_TEXT_KEY));
                }
                latch.countDown(); // 释放锁

            }
        };

        // 运行 Dify 工作流（流式输出模式）
        client.runWorkflowStream(request, callback);
        // 等待工作流完成，或直到超时
        latch.await(TIMEOUT_MINUTES, TimeUnit.MINUTES);

        String result = outputBuilder.toString();
        // 如果输出包含 JSON 标记（```json），则将其移除
        if (result.contains("```json")) {
            result = result.replace("```json", "").replace("```", "");
        }
        // 将处理后的结果传递给消费者
        outputConsumer.accept(result);
    }

    /**
     * 用于图像题目识别的工作流方法。
     * 通过 Dify 工作流识别图片中的题目内容。
     *
     * @param fileUrl 图像文件的远程 URL。
     * @return 从工作流输出中解析得到的 SaveQuestion 对象列表。
     * @throws IOException          如果发生 I/O 错误。
     * @throws InterruptedException 如果当前线程在等待时被中断。
     */
    public static List<SaveQuestion> imageQuestionWorkflow(String fileUrl) throws IOException, InterruptedException {
        WorkflowRunRequest workflowRunRequest = new WorkflowRunRequest();
        FileInfo fileInfo = new FileInfo();
        fileInfo.setType(FileType.IMAGE); // 文件类型：图片
        fileInfo.setTransferMethod(FileTransferMethod.REMOTE_URL); // 传输方式：远程URL
        fileInfo.setUrl(fileUrl);
        workflowRunRequest.setInputs(Map.of("question", fileInfo)); // 设置输入参数：题目图片信息
        workflowRunRequest.setUser(USER_ADMIN); // 设置用户

        final String[] questionJson = new String[1];
        // 执行工作流并等待结果，将结果存储到 questionJson 数组中
        runWorkflowAndAwaitCompletion(DifyClientUtil.imageQuestionWorkflowClient, workflowRunRequest, output -> questionJson[0] = output);

        // 将 JSON 字符串解析为 SaveQuestion 对象的列表
        return JSON.parseArray(questionJson[0], SaveQuestion.class);
    }

    /**
     * 获取题目答案的工作流方法。
     * 通过 Dify 工作流根据题目信息获取答案。
     *
     * @param questionAnswerReq 包含题目详细信息的请求对象。
     * @return 从工作流输出中解析得到的 SaveQuestion 对象。
     * @throws IOException          如果发生 I/O 错误。
     * @throws InterruptedException 如果当前线程在等待时被中断。
     */
    public static SaveQuestion questionAnswerWorkflow(QuestionAnswerReq questionAnswerReq) throws IOException, InterruptedException {
        WorkflowRunRequest workflowRunRequest = new WorkflowRunRequest();
        if (questionAnswerReq.getMaterials() == null) {
            questionAnswerReq.setMaterials("");
        }
        if (questionAnswerReq.getSelect() == null) {
            questionAnswerReq.setSelect("");
        }
        // 设置输入参数：题目标题、材料、选项和类型
        workflowRunRequest.setInputs(Map.of("questionTitle", questionAnswerReq
            .getQuestionTitle(), "materials", questionAnswerReq.getMaterials(), "select", questionAnswerReq
                .getSelect(), "type", questionAnswerReq.getType()));
        workflowRunRequest.setUser(USER_ADMIN); // 设置用户

        final String[] questionJson = new String[1];
        // 执行工作流并等待结果
        runWorkflowAndAwaitCompletion(DifyClientUtil.questionAnswerWorkflowClient, workflowRunRequest, output -> questionJson[0] = output);

        // 将 JSON 字符串解析为 SaveQuestion 对象
        return JSON.parseObject(questionJson[0], SaveQuestion.class);
    }

    /**
     * 获取题目知识点的工作流方法。
     * 通过 Dify 工作流识别题目相关的知识点。
     *
     * @param question  题目字符串。
     * @param knowledge 相关的知识点字符串（可能作为上下文或现有知识点）。
     * @return 从工作流输出中解析得到的知识点（字符串）列表。
     * @throws IOException          如果发生 I/O 错误。
     * @throws InterruptedException 如果当前线程在等待时被中断。
     */
    public static List<String> questionKnowledgeWorkflow(String question,
                                                         String knowledge) throws IOException, InterruptedException {
        WorkflowRunRequest workflowRunRequest = new WorkflowRunRequest();
        // 设置输入参数：题目和知识点
        workflowRunRequest.setInputs(Map.of("question", question, "knowledge", knowledge));
        workflowRunRequest.setUser(USER_ADMIN); // 设置用户

        final String[] knowledgeJson = new String[1];
        // 执行工作流并等待结果
        runWorkflowAndAwaitCompletion(DifyClientUtil.questionKnowledgeWorkflowClient, workflowRunRequest, output -> knowledgeJson[0] = output);

        // 将 JSON 字符串解析为字符串列表
        return JSON.parseArray(knowledgeJson[0], String.class);
    }

    /**
     * 根据知识点生成题目的工作流方法。
     * 通过 Dify 工作流根据指定的知识点、类目、数量和题目类型生成题目。
     *
     * @param questionGenerateReq 包含生成题目所需参数的请求对象。
     * @return 从工作流输出中解析得到的 SaveQuestion 对象列表。
     * @throws IOException          如果发生 I/O 错误。
     * @throws InterruptedException 如果当前线程在等待时被中断。
     */
    public static List<SaveQuestion> questionGenerateWorkflow(QuestionGenerateReq questionGenerateReq) throws IOException, InterruptedException {
        WorkflowRunRequest workflowRunRequest = new WorkflowRunRequest();

        // 参数校验和默认值设置
        if (questionGenerateReq.getKnowledge() == null) {
            questionGenerateReq.setKnowledge("");
        }
        if (questionGenerateReq.getType() == null) {
            questionGenerateReq.setType("");
        }
        if (questionGenerateReq.getSum() == null || questionGenerateReq.getSum() <= 0) {
            questionGenerateReq.setSum(1);
        }
        if (questionGenerateReq.getQuestionType() == null) {
            questionGenerateReq.setQuestionType("");
        }
        if (questionGenerateReq.getSummary() == null) {
            questionGenerateReq.setSummary("");
        }
        // 设置输入参数：知识点、类目、数量、题目类型
        workflowRunRequest.setInputs(Map.of("knowledge", questionGenerateReq.getKnowledge(), "type", questionGenerateReq
            .getType(), "sum", questionGenerateReq.getSum().toString(), "question_type", questionGenerateReq
                .getQuestionType(), "summary", questionGenerateReq.getSummary()));
        workflowRunRequest.setUser(USER_ADMIN); // 设置用户

        final String[] questionJson = new String[1];
        // 执行工作流并等待结果
        runWorkflowAndAwaitCompletion(DifyClientUtil.questionGenerateWorkflowClient, workflowRunRequest, output -> questionJson[0] = output);

        // 将 JSON 字符串解析为 SaveQuestion 对象列表
        return JSON.parseArray(questionJson[0], SaveQuestion.class);
    }

    /**
     * 根据题目用户答案，总结学习成果的工作流方法。
     * 通过 Dify 工作流分析用户的答题情况，总结学习成果和提供学习建议。
     *
     * @param questionSummaryReq 包含题目和用户答案的请求对象。
     * @return 从工作流输出中解析得到的学习总结对象。
     * @throws IOException          如果发生 I/O 错误。
     * @throws InterruptedException 如果当前线程在等待时被中断。
     */
    public static QuestionSummaryResp questionSummaryWorkflow(QuestionSummaryReq questionSummaryReq) throws IOException, InterruptedException {
        WorkflowRunRequest workflowRunRequest = new WorkflowRunRequest();

        // 参数校验和默认值设置
        if (questionSummaryReq.getQuestionAndAnswer() == null) {
            questionSummaryReq.setQuestionAndAnswer("");
        }

        if (questionSummaryReq.getKnowledge() == null) {
            questionSummaryReq.setKnowledge("");
        }

        // 设置输入参数：题目和答案、知识点
        workflowRunRequest.setInputs(Map.of("questionAndAnswer", questionSummaryReq
            .getQuestionAndAnswer(), "knowledge", questionSummaryReq.getKnowledge()));
        workflowRunRequest.setUser(USER_ADMIN); // 设置用户

        final String[] summaryResult = new String[1];
        // 执行工作流并等待结果
        runWorkflowAndAwaitCompletion(DifyClientUtil.questionSummaryWorkflowClient, workflowRunRequest, output -> summaryResult[0] = output);

        try {
            // 将JSON字符串解析为QuestionSummaryResp对象
            return JSON.parseObject(summaryResult[0], QuestionSummaryResp.class);
        } catch (Exception e) {
            // 解析失败时创建一个默认的响应对象，将原始结果放入summarize字段
            QuestionSummaryResp resp = new QuestionSummaryResp();
            resp.setSummarize(summaryResult[0]);
            resp.setOverallAssessment("");
            return resp;
        }
    }

}